# -*- coding: utf-8 -*-
"""
存放有关MCSTRUCTURE结构操作的内容
"""

"""
注意，此文件未经允许不得商用

此文件中
部分函数拷贝或修改自 音·创
音·创 代码仓库地址为：
https://gitee.com/TriM-Organization/Musicreater

本程序依照 Apache2.0 协议标明其作者，源协议文件位于 `https://gitee.com/TriM-Organization/Musicreater/blob/master/LICENSE.md`

引用协议：

   版权所有 © 2023 音·创 开发者
   Copyright © 2023 all the developers of Musicreater

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

继承协议：

版权所有 © 2023 睿乐组织 金羿("Eilles Wan") & 所有 芷词 开发者
Copyright © 2023 TriM-Organization EillesWan(W-YI_DoctorYI@outlook.com) and all the developers of LyricAngelica

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

"""


# 睿穆组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com


import math
from dataclasses import dataclass
from typing import List, Literal, Tuple

from TrimMCStruct import Block, Structure, TAG_Byte, TAG_Long


@dataclass(init=False)
class SingleCommand:
    """存储单个指令的类"""

    command_text: str
    """指令文本"""

    conditional: bool
    """执行是否有条件"""

    delay: int
    """执行的延迟"""

    annotation_text: str
    """指令注释"""

    def __init__(
        self,
        command: str,
        condition: bool = False,
        tick_delay: int = 0,
        annotation: str = "",
    ):
        """
        存储单个指令的类

        Parameters
        ----------
        command: str
            指令
        condition: bool
            是否有条件
        tick_delay: int
            执行延时
        annotation: str
            注释
        """
        self.command_text = command
        self.conditional = condition
        self.delay = tick_delay
        self.annotation_text = annotation

    def copy(self):
        return SingleCommand(
            command=self.command_text,
            condition=self.conditional,
            tick_delay=self.delay,
            annotation=self.annotation_text,
        )

    @property
    def cmd(self) -> str:
        """
        我的世界函数字符串（包含注释）
        """
        return self.__str__()

    def __str__(self) -> str:
        """
        转为我的世界函数文件格式（包含注释）
        """
        return "#[{cdt}]<{delay}> {ant}\n{cmd}".format(
            cdt="CDT" if self.conditional else "",
            delay=self.delay,
            ant=self.annotation_text,
            cmd=self.command_text,
        )

    def __eq__(self, other) -> bool:
        if not isinstance(other, self.__class__):
            return False
        return self.__str__() == other.__str__()


def bottem_side_length_of_smallest_square_bottom_box(total: int, maxHeight: int):
    """给定总方块数量和最大高度，返回所构成的图形外切正方形的边长
    :param total: 总方块数量
    :param maxHeight: 最大高度
    :return: 外切正方形的边长 int"""
    return math.ceil(math.sqrt(math.ceil(total / maxHeight)))


x = "x"
y = "y"
z = "z"


def antiaxis(axis: Literal["x", "z", "X", "Z"]):
    return z if axis == x else x


def forward_IER(forward: bool):
    return 1 if forward else -1


AXIS_PARTICULAR_VALUE = {
    x: {
        True: 5,
        False: 4,
    },
    y: {
        True: 1,
        False: 0,
    },
    z: {
        True: 3,
        False: 2,
    },
}


def command_statevalue(axis_: Literal["x", "y", "z", "X", "Y", "Z"], forward_: bool):
    return AXIS_PARTICULAR_VALUE[axis_.lower()][forward_]


def form_repeater_in_NBT_struct(delay: int, facing: int):
    """生成中继器方块
    :param facing: 朝向：
        Z- 北 0
        X- 东 1
        Z+ 南 2
        X+ 西 3
    :param delay: 0~3
    :return Block()"""

    return Block(
        "minecraft",
        "unpowered_repeater",
        {
            "repeater_delay": delay,
            "direction": facing,
        },
    )


def form_command_block_in_NBT_struct(
    command: str,
    coordinate: tuple,
    particularValue: int,
    impluse: int = 0,
    condition: bool = False,
    alwaysRun: bool = True,
    tickDelay: int = 0,
    customName: str = "",
    executeOnFirstTick: bool = False,
    trackOutput: bool = True,
):
    """
    使用指定项目返回指定的指令方块结构
    :param command: `str`
        指令
    :param coordinate: `tuple[int,int,int]`
        此方块所在之相对坐标
    :param particularValue:
        方块特殊值，即朝向
            :0	下	无条件
            :1	上	无条件
            :2	z轴负方向	无条件
            :3	z轴正方向	无条件
            :4	x轴负方向	无条件
            :5	x轴正方向	无条件
            :6	下	无条件
            :7	下	无条件

            :8	下	有条件
            :9	上	有条件
            :10	z轴负方向	有条件
            :11	z轴正方向	有条件
            :12	x轴负方向	有条件
            :13	x轴正方向	有条件
            :14	下	有条件
            :14	下	有条件
        注意！此处特殊值中的条件会被下面condition参数覆写
    :param impluse: `int 0|1|2`
        方块类型
            0脉冲 1循环 2连锁
    :param condition: `bool`
        是否有条件
    :param alwaysRun: `bool`
        是否始终执行
    :param tickDelay: `int`
        执行延时
    :param customName: `str`
        悬浮字
    :param executeOnFirstTick: `bool`
        首刻执行(循环指令方块是否激活后立即执行，若为False，则从激活时起延迟后第一次执行)
    :param trackOutput: `bool`
        是否输出

    :return:str
    """

    return Block(
        "minecraft",
        "command_block"
        if impluse == 0
        else ("repeating_command_block" if impluse == 1 else "chain_command_block"),
        states={"conditional_bit": condition, "facing_direction": particularValue},
        extra_data={
            "block_entity_data": {
                "Command": command,
                "CustomName": customName,
                "ExecuteOnFirstTick": executeOnFirstTick,
                "LPCommandMode": 0,
                "LPCondionalMode": False,
                "LPRedstoneMode": False,
                "LastExecution": TAG_Long(0),
                "LastOutput": "",
                "LastOutputParams": [],
                "SuccessCount": 0,
                "TickDelay": tickDelay,
                "TrackOutput": trackOutput,
                "Version": 25,
                "auto": alwaysRun,
                "conditionMet": False,  # 是否已经满足条件
                "conditionalMode": condition,
                "id": "CommandBlock",
                "isMovable": True,
                "powered": False,  # 是否已激活
                "x": coordinate[0],
                "y": coordinate[1],
                "z": coordinate[2],
            }  # type: ignore
        },
        compability_version=17959425,
    )


def commands_to_structure(
    commands: List[SingleCommand],
    max_height: int = 64,
):
    """
    :param commands: 指令列表
    :param max_height: 生成结构最大高度
    :return 结构类,结构占用大小,终点坐标
    """

    _sideLength = bottem_side_length_of_smallest_square_bottom_box(
        len(commands), max_height
    )

    struct = Structure(
        (_sideLength, max_height, _sideLength),  # 声明结构大小
    )

    y_forward = True
    z_forward = True

    now_y = 0
    now_z = 0
    now_x = 0

    for command in commands:
        coordinate = (now_x, now_y, now_z)
        struct.set_block(
            coordinate,
            form_command_block_in_NBT_struct(
                command=command.command_text,
                coordinate=coordinate,
                particularValue=(1 if y_forward else 0)
                if (
                    ((now_y != 0) and (not y_forward))
                    or (y_forward and (now_y != (max_height - 1)))
                )
                else (
                    (3 if z_forward else 2)
                    if (
                        ((now_z != 0) and (not z_forward))
                        or (z_forward and (now_z != _sideLength - 1))
                    )
                    else 5
                ),
                impluse=2,
                condition=False,
                alwaysRun=True,
                tickDelay=command.delay,
                customName=command.annotation_text,
                executeOnFirstTick=False,
                trackOutput=True,
            ),
        )

        now_y += 1 if y_forward else -1

        if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
            now_y -= 1 if y_forward else -1

            y_forward = not y_forward

            now_z += 1 if z_forward else -1

            if ((now_z >= _sideLength) and z_forward) or (
                (now_z < 0) and (not z_forward)
            ):
                now_z -= 1 if z_forward else -1
                z_forward = not z_forward
                now_x += 1

    return (
        struct,
        (
            now_x + 1,
            max_height if now_x or now_z else now_y,
            _sideLength if now_x else now_z,
        ),
        (now_x, now_y, now_z),
    )


def commands_to_redstone_delay_structure(
    commands: List[SingleCommand],
    delay_length: int,
    max_multicmd_length: int,
    base_block: str = "concrete",
    axis_: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
) -> Tuple[Structure, Tuple[int, int, int], Tuple[int, int, int]]:
    """
    :param commands: 指令列表
    :param delay_length: 延时总长
    :param max_multicmd_length: 最大同时播放的音符数量
    :param base_block: 生成结构的基底方块
    :param axis_: 生成结构的延展方向
    :return 结构类,结构占用大小,终点坐标
    """
    if axis_ in ["z+", "Z+"]:
        extensioon_direction = z
        aside_direction = x
        repeater_facing = 2
        forward = True
    elif axis_ in ["z-", "Z-"]:
        extensioon_direction = z
        aside_direction = x
        repeater_facing = 0
        forward = False
    elif axis_ in ["x+", "X+"]:
        extensioon_direction = x
        aside_direction = z
        repeater_facing = 3
        forward = True
    elif axis_ in ["x-", "X-"]:
        extensioon_direction = x
        aside_direction = z
        repeater_facing = 1
        forward = False
    else:
        raise ValueError(f"axis_({axis_}) 参数错误。")

    goahead = forward_IER(forward)

    struct = Structure(
        (
            round(delay_length / 2 + 0.5 + len(commands))
            if extensioon_direction == x
            else max_multicmd_length,
            3,
            round(delay_length / 2 + 0.5 + len(commands))
            if extensioon_direction == z
            else max_multicmd_length,
        )
    )

    pos_now = {
        x: (0 if forward else struct.size[0]),
        y: 0,
        z: (0 if forward else struct.size[2]),
    }

    first_impluse = True

    for cmd in commands:
        single_repeater_value = round(cmd.delay / 2) % 4 - 1
        additional_repeater = round(cmd.delay / 2) // 4
        for i in range(additional_repeater):
            struct.set_block(
                tuple(pos_now.values()),  # type: ignore
                Block(
                    "minecraft",
                    base_block,
                ),
            )
            struct.set_block(
                (pos_now[x], 1, pos_now[z]),
                form_repeater_in_NBT_struct(
                    delay=3,
                    facing=repeater_facing,
                ),
            )
            pos_now[extensioon_direction] += goahead
            first_impluse = True
        if single_repeater_value >= 0:
            struct.set_block(
                tuple(pos_now.values()),  # type: ignore
                Block(
                    "minecraft",
                    base_block,
                ),
            )
            struct.set_block(
                (pos_now[x], 1, pos_now[z]),
                form_repeater_in_NBT_struct(
                    delay=single_repeater_value,
                    facing=repeater_facing,
                ),
            )
            pos_now[extensioon_direction] += goahead
            first_impluse = True
        struct.set_block(
            (pos_now[x], 1, pos_now[z]),
            form_command_block_in_NBT_struct(
                command=cmd.command_text,
                coordinate=(pos_now[x], 1, pos_now[z]),
                particularValue=command_statevalue(extensioon_direction, forward),
                # impluse= (0 if first_impluse else 2),
                impluse=0,
                condition=False,
                alwaysRun=False,
                tickDelay=0,
                customName=cmd.annotation_text,
            ),
        )
        struct.set_block(
            (pos_now[x], 2, pos_now[z]),
            Block("minecraft", "redstone_wire"),
        )
        pos_now[extensioon_direction] += goahead

        first_impluse = False

    return struct, struct.size, tuple(pos_now.values())  # type: ignore
